package com.zhudi.math;

import com.ruoyi.common.exception.BusinessException;
import com.zhudi.utils.MathUtil;
import com.zhudi.utils.ZhuDiUtil;

import java.math.BigDecimal;

/**
 * 客观权重计算
 * @author chongjin
 */
public class Objective {

    /**
     * 标准化矩阵
     * @param original 初始矩阵
     * @return BigDecimal[][] 标准化矩阵
     */
    public static BigDecimal[][] tranStandard(BigDecimal[][] original) {
        //构造一个参数矩阵用来存放每一列的最大值和最小值
        BigDecimal[][] param = new BigDecimal[2][original[0].length];
        BigDecimal max = null;
        BigDecimal min = null;
        //遍历每一列
        for (int j = 0; j < original[0].length; j++) {
            max = BigDecimal.ZERO;
            min = MathUtil.MAX;
            //排序得到最大值和最小值
            for (int i = 0; i < original.length; i++) {
                max = max.compareTo(original[i][j]) > 0 ? max : original[i][j];
                min = min.compareTo(original[i][j]) < 0 ? min : original[i][j];
            }
            param[0][j] = max;
            param[1][j] = min;
        }
        BigDecimal sumStandard = null;
        BigDecimal subtract = null;
        //新构造一个矩阵
        BigDecimal[][] standard = new BigDecimal[original.length][original[0].length];
        //遍历新的矩阵
        for (int j = 0; j < standard[0].length; j++) {
            max = param[0][j];
            min = param[1][j];
            subtract = max.subtract(min);
            sumStandard = BigDecimal.ZERO;
            for (int i = 0; i < standard.length; i++) {
                //正向指标标准化
                if (j < ZhuDiUtil.POSITIVE) {
                    standard[i][j] = original[i][j].subtract(min).divide(subtract, 20, BigDecimal.ROUND_HALF_UP);
                }
                //负向指标标准化
                else {
                    standard[i][j] = max.subtract(original[i][j]).divide(subtract, 20, BigDecimal.ROUND_HALF_UP);
                }
                //保存处理后的X矩阵
                ZhuDiUtil.standardX[i][j] = standard[i][j];
                //每列标准化数值平方和
                sumStandard = sumStandard.add(standard[i][j].pow(2));
            }
            for (int i = 0; i < standard.length; i++) {
                //标准化
                standard[i][j] = standard[i][j].divide(MathUtil.getSqrt(sumStandard), 4, BigDecimal.ROUND_HALF_UP);
            }
        }
        return standard;
    }

    /**
     * 计算各指标客观权重
     * @param standardX 正负处理矩阵X
     * @return BigDecimal[] 客观权重向量
     */
    public static BigDecimal[] getObject(BigDecimal[][] standardX) {
        BigDecimal sumValueJ = null;
        BigDecimal sumEntropyJ = null;
        BigDecimal usefulValue = null;
        BigDecimal entropyJ = null;
        BigDecimal sumUsefulValue = BigDecimal.ZERO;
        //计算 -1/lni
        BigDecimal param = BigDecimal.ONE.divide(MathUtil.getLn(new BigDecimal(standardX.length)), 4, BigDecimal.ROUND_HALF_UP).negate();
        BigDecimal[] resultData = new BigDecimal[standardX[0].length];
        for (int j = 0; j < standardX[0].length; j++) {
            sumValueJ = BigDecimal.ZERO;
            sumEntropyJ = BigDecimal.ZERO;
            //每个指标所有值求和
            for (int i = 0; i < standardX.length; i++) {
                sumValueJ = sumValueJ.add(standardX[i][j]);
            }
            //计算样本比重
            for (int i = 0; i < standardX.length; i++) {
                entropyJ = standardX[i][j].divide(sumValueJ, 4, BigDecimal.ROUND_HALF_UP);
                sumEntropyJ = sumEntropyJ.add(entropyJ.multiply(MathUtil.getLn(entropyJ)));
            }
            //计算熵值以及效用值
            usefulValue = BigDecimal.ONE.subtract(param.multiply(sumEntropyJ));
            //效用值求和
            sumUsefulValue = sumUsefulValue.add(usefulValue);
            resultData[j] = usefulValue;
        }
        //
        BigDecimal percentWeight = null;
        //得到权重
        for (int j = 0; j < standardX[0].length; j++) {
            percentWeight = resultData[j].divide(sumUsefulValue, 4, BigDecimal.ROUND_HALF_UP);
            resultData[j] = percentWeight;
        }
        return resultData;
    }

    /**
     * 计算综合权重
     * @param subjectiveData 主观权重向量
     * @param objectiveData  客观权重向量
     * @return BigDecimal[] 综合权重向量
     */
    public static BigDecimal[] getComprehensive(BigDecimal[] subjectiveData, BigDecimal[] objectiveData) {
        if (subjectiveData.length != objectiveData.length) {
            throw new BusinessException("主观和客观权重向量长度不一致");
        }
        BigDecimal sumMultiply = BigDecimal.ZERO;
        //客观*主观 累加
        for (int i = 0; i < subjectiveData.length; i++) {
            sumMultiply = sumMultiply.add(subjectiveData[i].multiply(objectiveData[i]));
        }
        BigDecimal[] resultData = new BigDecimal[subjectiveData.length];
        //得到综合权重
        for (int i = 0; i < subjectiveData.length; i++) {
            resultData[i] = subjectiveData[i].multiply(objectiveData[i]).divide(sumMultiply, 4, BigDecimal.ROUND_HALF_UP);
        }
        return resultData;
    }
}
